/*******************************************************************************
 * Copyright (c) 2006 CEA List.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     CEA List - initial API and implementation
 *******************************************************************************/
package com.cea.papyrus.diagram.statemachine.editpart;

import java.util.Iterator;
import java.util.List;

import org.eclipse.draw2d.IFigure;
import org.eclipse.draw2d.XYLayout;
import org.eclipse.draw2d.geometry.Dimension;
import org.eclipse.draw2d.geometry.Point;
import org.eclipse.draw2d.geometry.PrecisionRectangle;
import org.eclipse.draw2d.geometry.Rectangle;
import org.eclipse.gef.EditPart;
import org.eclipse.gef.EditPolicy;
import org.eclipse.gef.Request;
import org.eclipse.gef.RequestConstants;
import org.eclipse.gef.commands.Command;
import org.eclipse.gef.commands.CompoundCommand;
import org.eclipse.gef.requests.ChangeBoundsRequest;
import org.eclipse.gef.requests.CreateRequest;
import org.eclipse.uml2.uml.ConnectionPointReference;
import org.eclipse.uml2.uml.Element;
import org.eclipse.uml2.uml.Pseudostate;
import org.eclipse.uml2.uml.Region;
import org.eclipse.uml2.uml.State;

import com.cea.papyrus.core.commands.AddUMLElementToDiagram;
import com.cea.papyrus.core.commands.Di2UMLElementCreateCommand;
import com.cea.papyrus.core.commands.GraphNodeSetConstraintCommand;
import com.cea.papyrus.core.commands.UMLElementAddCommand;
import com.cea.papyrus.core.dnd.ObjectTransfer;
import com.cea.papyrus.core.editpart.LayoutEditPolicy;
import com.cea.papyrus.core.helper.NodeHelper;
import com.cea.papyrus.core.pseudoelement.EnhancedType;
import com.cea.papyrus.core.utils.Debug;
import com.cea.papyrus.core.utils.LookForElement;
import com.cea.papyrus.diagram.statemachine.command.AddRegionNodeCommand;
import com.cea.papyrus.diagram.statemachine.command.CreateRegionCommand;
import com.cea.papyrus.diagram.statemachine.command.ResizeRegionNodeCommand;
import com.cea.papyrus.diagram.statemachine.command.ResizeStateMachineNodeCommand;
import com.cea.papyrus.diagram.statemachine.command.ResizeStateNodeCommand;
import com.cea.papyrus.diagram.statemachine.command.UMLDi2ElementCreateFactory;
import com.cea.papyrus.diagram.statemachine.figure.RegionFigure;
import com.cea.papyrus.diagram.statemachine.helper.NodeConnectionPointReferenceHelper;
import com.cea.papyrus.diagram.statemachine.helper.NodePointHelper;
import com.cea.papyrus.diagram.statemachine.helper.NodeRegionHelper.Zone;
import com.cea.papyrus.diagraminterchange2.di2.Di2Factory;
import com.cea.papyrus.diagraminterchange2.di2.GraphElement;
import com.cea.papyrus.diagraminterchange2.di2.GraphNode;
import com.cea.papyrus.diagraminterchange2.di2.Property;
import com.cea.papyrus.diagraminterchange2.di2.Uml1SemanticModelBridge;

/**
 * The Class StateMachineLayoutEditPolicy.
 * Associates various edit policies to specific EditParts involved in state machine diagrams
 * routes calls for Command creation to the specific ones if any.
 * 
 * @author David Servat
 * @since 29 jan. 07
 */
public class StateMachineLayoutEditPolicy extends LayoutEditPolicy{

	/**
	 * The channel.
	 */
	private int channel = 501;

	/**
	 * The Constructor.
	 * 
	 * @param layout the layout
	 */
	public StateMachineLayoutEditPolicy(XYLayout layout){
		super(layout);
		Debug.debug(this, "Call StateMachineLayoutEditPolicy(XYLayout layout)", channel);
	}

	/**
	 * Provides additional policies to child edit parts, namely a specific ResizableEditPolicy
	 * to RegionEditPart and SplitterEditPart to constrain the allowed resize directions.
	 * Others will have the generic edit policies.
	 * 
	 * @param child a child EditPart
	 * 
	 * @return 
	 */
	@Override
	protected EditPolicy createChildEditPolicy(EditPart child){
		Debug.debug(this, "Call StateMachineLayoutEditPolicy.createChildEditPolicy(EditPart child)", channel);

		//specific resize edit policy for regions
		//we want to avoid resize in oblique directions such as NORTH_EAST
		//and restrict resize to inner expansion
		//which do not cause a resize of the embedding statemachine
		if(child instanceof RegionEditPart){
			ResizableEditPolicy policy = new ResizableEditPolicy();
			//retrieve the graph node associated
			GraphNode node = (GraphNode)child.getModel();
			//its zone property
			String zone = ((Property)node.getProperty("zone")).getValue();
			//test for allowed directions
			int dirs = Zone.getAllowedResizeDirections(zone);
			//constrain the edit policy with these directions
			policy.setResizeDirections(dirs);
			return policy;
		}
		//exclude any resize edit policy for splitter nodes
		//in case they might appear due to minor fault in the resize
		else if(child instanceof SplitterEditPart){
			ResizableEditPolicy policy = new ResizableEditPolicy();
			policy.setResizeDirections(0);
			return policy;
		}
		//all others, namely state machines and states, pseudostates, etc.
		//will have the generic policy which allows resize in all directions
		return super.createChildEditPolicy(child);
	}

	/**
	 * We do not allow for a resize command without the appropriate request, thus return null.
	 * 
	 * @param child 
	 * @param constraint 
	 * 
	 * @return 
	 * 
	 * @see org.eclipse.gef.editpolicies.ConstrainedLayoutEditPolicy#createChangeConstraintCommand(org.eclipse.gef.EditPart, java.lang.Object)
	 */
	@Override
	protected Command createChangeConstraintCommand(EditPart child, Object constraint) {
		return null;
	}

	/**
	 * This method analyzes a resize and/or move request and routes the call to the appropriate
	 * specific ResizeCommand either for a StateMachine, a State or a Region, in other cases passes on the call to the
	 * super class method.
	 * 
	 * @param child the concerned EditPart
	 * @param constraint the object embedding the contrasint rectangle
	 * @param request the ChangeBoundsRequest
	 * 
	 * @return 
	 */
	@Override
	protected Command createChangeConstraintCommand(ChangeBoundsRequest request, EditPart child, Object constraint) {
		Debug.debug(this, "Call StateMachineLayoutEditPolicy.createChangeConstraintCommand(ChangeBoundsRequest request, EditPart child, Object constraint)", channel);

		//precautionary test
		if((request == null) || (child == null) || (constraint == null))
			throw new IllegalArgumentException();

		//retrieve the deltas of the resize request
		int dx = request.getSizeDelta().width;
		int dy = request.getSizeDelta().height;
		CompoundCommand command = new CompoundCommand();
		if(child instanceof StateMachineEditPart){
			if((dx == 0) && (dy == 0)){
				//nothing to do except move the state machine edit part
				//all locations of embedded figures are relative to it
				//and thus hold when their container is moved around
				command.add(new GraphNodeSetConstraintCommand((GraphNode)child.getModel(), request, (Rectangle)constraint));
			}
			else{
				//a resize request, which we route to the specific ResizeCommand
				GraphNode statemachine = (GraphNode)child.getModel();

				command.add(new ResizeStateMachineNodeCommand(statemachine, request, (Rectangle)constraint));
			}
			//move the entry/exit points if any
			Iterator itgn = ((GraphNode)child.getModel()).getSemanticChild().iterator();
			while (itgn.hasNext()){
				GraphElement ge = (GraphElement)itgn.next();
				if((ge.getSemanticModel() != null) 
						&& (((Uml1SemanticModelBridge)ge.getSemanticModel()).getElement() instanceof Pseudostate)){
					//ge is a graphNode of an entry/exit point
					GraphNode gn = (GraphNode)ge;
					// calculate new position of the point 
					Rectangle rec = calculateNewPosition((GraphNode)child.getModel(), gn, request);
					GraphNodeSetConstraintCommand locationCommand = new GraphNodeSetConstraintCommand(gn, request, rec);
					//add this command in the list of commands
					command.add(locationCommand);

					//look for all portNameEditPArt graphNode
					Iterator itPortName = gn.getSemanticChild().iterator();
					Dimension diff = rec.getLocation().getDifference(gn.getPosition());
					//look for all graphNode that is connected to propertygn
					while (itPortName.hasNext()){
						GraphNode gePortName=(GraphNode)itPortName.next();
						//Create a move command for this element
						rec =new Rectangle(gePortName.getPosition(),gePortName.getSize());
						rec=rec.getTranslated(diff.width,diff.height);

						locationCommand = new GraphNodeSetConstraintCommand((GraphNode)gePortName, request, rec);
						// add this command in the list of commands
						command.add(locationCommand);
					}
				}	
			}
			displayCompoundCommand(command);
			return command;			
		}
		else if(child instanceof StateEditPart){
			if((dx == 0) && (dy == 0)){
				//nothing to do except move the state edit part
				//all locations of embedded figures are relative to it
				//and thus hold when their container is moved around
				command.add(new GraphNodeSetConstraintCommand((GraphNode)child.getModel(), request, (Rectangle)constraint));
			}
			else{
				//a resize request, which we route to the specific ResizeCommand
				GraphNode state = (GraphNode)child.getModel();

				command.add(new ResizeStateNodeCommand(state, request, (Rectangle)constraint));
			}
			//move the connection point references and entry/exit points if any
			Iterator itgn = ((GraphNode)child.getModel()).getSemanticChild().iterator();
			while (itgn.hasNext()){
				GraphElement ge = (GraphElement)itgn.next();
				if((ge.getSemanticModel() != null) 
						&& ((((Uml1SemanticModelBridge)ge.getSemanticModel()).getElement() instanceof ConnectionPointReference)
								|| (((Uml1SemanticModelBridge)ge.getSemanticModel()).getElement() instanceof Pseudostate))){
					//ge is a graphNode of a connection point reference or entry/exit point
					GraphNode gn = (GraphNode)ge;
					// calculate new position of the connection point reference
					Rectangle rec = calculateNewPosition((GraphNode)child.getModel(), gn, request);
					GraphNodeSetConstraintCommand locationCommand = new GraphNodeSetConstraintCommand(gn, request, rec);
					//add this command in the list of commands
					command.add(locationCommand);

					//look for all portNameEditPArt graphNode
					Iterator itPortName = gn.getSemanticChild().iterator();
					Dimension diff = rec.getLocation().getDifference(gn.getPosition());
					//look for all graphNode that is connected to propertygn
					while (itPortName.hasNext()){
						GraphNode gePortName=(GraphNode)itPortName.next();
						//Create a move command for this element
						rec =new Rectangle(gePortName.getPosition(),gePortName.getSize());
						rec=rec.getTranslated(diff.width,diff.height);

						locationCommand = new GraphNodeSetConstraintCommand((GraphNode)gePortName, request, rec);
						// add this command in the list of commands
						command.add(locationCommand);
					}
				}	
			}
			displayCompoundCommand(command);
			return command;
		}
		else if(child instanceof RegionEditPart){
			if((dx == 0) && (dy == 0)){
				//a tentative to move a region
				//this is not allowed
				//no command is passed on
				//CHECK how to prevent a shadow figure to be displayed
				return null;
			}
			else{
				//a resize request, which we route to the specific ResizeCommand
				GraphNode region = (GraphNode)child.getModel();

				return new ResizeRegionNodeCommand(region, request);
			}
		}
		//all other possible cases
		return super.createChangeConstraintCommand(request, child, constraint);
	}

	/**
	 * This method is used to set a new position of the graphNode when the parent GraphNode has moved.
	 * @param parentGraphNode the parentgraphNode that has moved.
	 * @param request the changeBoundRequest that the parent graphNode has received.
	 * @param connGraphNode the graphNode that represents the connection point reference or entry/exit point or the label of the GraphNode
	 * @return Rectangle that represents good position and size of the connGraphNode
	 */
	public Rectangle calculateNewPosition( GraphNode parentGraphNode, GraphNode connGraphNode, ChangeBoundsRequest request){
		Rectangle rec= new Rectangle();
		//detection of movement
		if(request.getSizeDelta().equals(new Dimension(0,0))){
			//apply translation
			rec.setSize(connGraphNode.getSize());
			rec.setLocation(connGraphNode.getPosition());
			rec.x=rec.x+request.getMoveDelta().x;
			rec.y=rec.y+request.getMoveDelta().y;
			return rec;			
		}

		//detection of a resize
		else{
			//1 calculus of intern limit.
			Rectangle smallParentRect = new Rectangle(
					parentGraphNode.getPosition().x+request.getMoveDelta().x,
					parentGraphNode.getPosition().y+request.getMoveDelta().y,
					parentGraphNode.getSize().width+request.getSizeDelta().width,
					parentGraphNode.getSize().height+request.getSizeDelta().height
			);
			//calculus of extern limit
			Rectangle bigParentRect = new Rectangle(
					parentGraphNode.getPosition().x+request.getMoveDelta().x-1,
					parentGraphNode.getPosition().y+request.getMoveDelta().y-1,
					parentGraphNode.getSize().width+request.getSizeDelta().width+2,
					parentGraphNode.getSize().height+request.getSizeDelta().height+2
			);

			//2 calculus of the middle of the connection point reference
			Point connMiddlePosition = new Point(
					connGraphNode.getPosition().x+ connGraphNode.getSize().width/2,
					connGraphNode.getPosition().y+connGraphNode.getSize().height/2
			);

			//3 if the point is in the extern rectangle and not in the intern limit the connection point reference is well placed
			if ((!smallParentRect.contains(connMiddlePosition))&& bigParentRect.contains(connMiddlePosition)){
				rec.setSize(connGraphNode.getSize());
				rec.setLocation(connGraphNode.getPosition());
				return rec;
			}
			//4 the port is badly placed
			else{
				NodeHelper nh = new NodeHelper();
				//we have to calculate position in the future position
				GraphNode futureNode = nh.createGraphElement(null, null, LookForElement.calculateAbsolutePosition(parentGraphNode,new Point(0,0)), parentGraphNode.getSize().getCopy());
				futureNode.setSize(futureNode.getSize().expand(request.getSizeDelta()));
				futureNode.setPosition(futureNode.getPosition().translate(request.getMoveDelta()));

				Debug.debug(this, "show position of future graphNode"+ futureNode.getPosition()+" "+futureNode.getSize(), channel);
				//calculate in absolute coordinates
				Point goodPosition = new Point(0,0);
				if(((Uml1SemanticModelBridge)connGraphNode.getSemanticModel()).getElement() instanceof ConnectionPointReference)
					goodPosition = (new NodeConnectionPointReferenceHelper()).inRunner(futureNode,LookForElement.calculateAbsolutePosition(connGraphNode,new Point(0,0)), connGraphNode.getSize());
				else if(((Uml1SemanticModelBridge)connGraphNode.getSemanticModel()).getElement() instanceof Pseudostate){
					Pseudostate pseudo = (Pseudostate)((Uml1SemanticModelBridge)connGraphNode.getSemanticModel()).getElement();
					goodPosition = (new NodePointHelper(pseudo.getKind())).inRunner(futureNode,LookForElement.calculateAbsolutePosition(connGraphNode,new Point(0,0)), connGraphNode.getSize());
				}
				// translate to relative
				Point origin = LookForElement.calculateAbsolutePosition(((GraphNode)parentGraphNode.getContainer()),new Point(0,0));
				goodPosition = goodPosition.getTranslated(origin.getNegated().getCopy());
				rec.setLocation(goodPosition);
				rec.setSize(connGraphNode.getSize());
				Debug.debug(this, "show new position of the connection point reference: "+rec.getLocation(), channel);
				return rec;
			}
		}
	}


	/**
	 * Both createAddCommand are overriden to introduce special processing for state machine diagrams.
	 * Namely to deal with region drag and drop within a state machine or a state.
	 * 
	 */
	@Override
	protected Command createLabelAddCommand(ChangeBoundsRequest request, GraphNode graphNode, Object constraint){
		return super.createLabelAddCommand(request, graphNode, constraint);
	}

	/**
	 * Both createAddCommand are overriden to introduce special processing for state machine diagrams.
	 * Namely to deal with region drag and drop within a state machine or a state.
	 * 
	 */
	@Override
	protected Command createAddCommand(ChangeBoundsRequest request, EditPart childEditPart, Object constraint){
		Debug.debug(this, "call StateMachineLayoutEditPolicy.createAddCommand(ChangeBoundsRequest request, EditPart childEditPart, Object constraint)", channel);
		if((childEditPart instanceof RegionEditPart) && (getHost() instanceof RegionEditPart)){
			GraphNode childNode = (GraphNode)childEditPart.getModel();
			GraphNode parentNode = (GraphNode)this.getHost().getModel();
			Element umlParent = null;
			Element umlChild = null;
			//retrieve the associated uml elements
			try{
				umlParent = LookForElement.lookForUMLElementOf(parentNode);
				umlChild = LookForElement.lookForUMLElementOf(childNode);
			}
			catch(Exception e){
				Debug.log(e);
			}
			CompoundCommand cmd = new CompoundCommand();
			UMLElementAddCommand umlAdd = new UMLElementAddCommand(umlParent, umlChild);
			cmd.add(umlAdd);
			Point location = request.getLocation().getCopy();
			this.getLayoutContainer().translateToRelative(location);
			this.getLayoutContainer().translateFromParent(location);
			location.translate(this.getLayoutOrigin().getNegated());
			AddRegionNodeCommand regionAdd = new AddRegionNodeCommand(childNode, parentNode, location);
			cmd.add(regionAdd);
			Debug.debug(this,"End of StateMachineLayoutEditPolicy.createAddCommand ", channel);
			return cmd;
		}
		else
			return super.createAddCommand(request, childEditPart, constraint);
	}

	
	/**
	 * Return specific factory to provide specific commands
	 */
	@Override
	protected UMLDi2ElementCreateFactory getCreateCommandFactory() {
	    return UMLDi2ElementCreateFactory.eINSTANCE;
	}


	/**
	 * getCreateCommand is overriden to introduce special processing for state machine diagrams.
	 * Namely to deal with region drag and drop within a state machine or a state.
	 * 
	 */
	@Override
	protected Command getCreateCommand(CreateRequest request){
		Debug.debug(this, "call Command StateMachineLayoutEditPolicy.getCreateCommand(CreateRequest request)", channel);
		if(!(getHost() instanceof RegionEditPart) && !(getHost() instanceof StateEditPart))
			return super.getCreateCommand(request);

		//then two possibilities
		//either a drag and drop
		if(request.getExtendedData().get(ObjectTransfer.UML_TRANSFER_OBJECT) != null){
			CompoundCommand command = new CompoundCommand();
			List elementList = (List)request.getExtendedData().get(ObjectTransfer.UML_TRANSFER_OBJECT);
			for(int i=0;i<elementList.size();i++){
				Element elt = (Element)elementList.get(i);
				GraphElement parent = (GraphElement)this.getHost().getModel();
				Point location = request.getLocation().getTranslated(i*120, 0);
				this.getLayoutContainer().translateToRelative(location);
				this.getLayoutContainer().translateFromParent(location);
				location.translate(this.getLayoutOrigin().getNegated());
				if(elt instanceof Region){
					if(getHost() instanceof RegionEditPart){
						//create a node for the region
						GraphNode aRegionNode = new NodeHelper().createGraphElement(elt, null, location, null);
						//assign default values for zone and dropLocation properties
						Property prop = Di2Factory.eINSTANCE.createProperty();
						prop.setKey("zone");
						prop.setValue(Zone.NONE);
						aRegionNode.getProperty().add(prop);

						prop = Di2Factory.eINSTANCE.createProperty();
						prop.setKey("dropLocation");
						prop.setValue(Zone.NONE);
						aRegionNode.getProperty().add(prop);

						//a command to update the uml parent
						Element newParent = ((Uml1SemanticModelBridge)((GraphNode)parent.getSemanticParent()).getSemanticModel()).getElement();
						command.add(new UMLElementAddCommand(newParent, elt));
						//a command to graphically add the region node
						command.add(new AddRegionNodeCommand(aRegionNode, (GraphNode)parent, location));
					}
					else if(getHost() instanceof StateEditPart){
						State state = (State)((Uml1SemanticModelBridge)((GraphNode)getHost().getModel()).getSemanticModel()).getElement();
						if(state.isSubmachineState())
							return null;
						command.add(new CreateRegionCommand((GraphNode)parent, location));
					}
				}
				else
					command.add(new AddUMLElementToDiagram(elt, parent, location, null));
			}
			command.setLabel("Add Into Diagram");
			return command;
		}

		//this is a simple creation request
		Object	newObjectDescription = EnhancedType.getNewObjectDescription(request);
		if(newObjectDescription instanceof Class){
			Class newObjectClass = (Class)newObjectDescription;
			if(newObjectClass.equals(Region.class)){
				GraphElement parent = (GraphElement)this.getHost().getModel();
				Point location = request.getLocation().getCopy();
				this.getLayoutContainer().translateToRelative(location);
				this.getLayoutContainer().translateFromParent(location);
				location.translate(this.getLayoutOrigin().getNegated());

				if(getHost() instanceof StateEditPart){
					State state = (State)((Uml1SemanticModelBridge)((GraphNode)getHost().getModel()).getSemanticModel()).getElement();
					if(state.isSubmachineState())
						return null;
				}
				return new CreateRegionCommand(parent, location);
			}
			else{
				Di2UMLElementCreateCommand createCommand = null;
				Point p;
				Debug.debug(this,"getCreateCommand:"+ newObjectDescription, channel);

				createCommand = getCreateCommandFactory().createCommand((Class)newObjectDescription, (GraphElement)this.getHost().getModel(), getCurrentTypeDiagram());
				createCommand.setCreateRequest(request);
				p = request.getLocation().getCopy();

				//if accelerator is used, the position is null
				if(p != null){
					//the element creation is done withe a position 
					this.getLayoutContainer().translateToRelative(p);
					this.getLayoutContainer().translateFromParent(p);
					p.translate(this.getLayoutOrigin().getNegated());
					createCommand.setLocation(p);
				}

				if(request.getSize()!=null){
					createCommand.setSize(request.getSize());
				}
				createCommand.setLabel("Create Element");
				return createCommand;

			}
		}
		return null;
	}



	/**
	 * The main routing method. Gets rid of Orphan ChangeBoundsRequest on RegionEditPart.
	 * Defaulted to the super class method elsewhere.
	 * @see org.eclipse.gef.editpolicies.ConstrainedLayoutEditPolicy#getCommand(org.eclipse.gef.Request)
	 */
	@Override
	public Command getCommand(Request request) {
		Debug.debug(this, "call StateMachineLayoutEditPolicy.getCommand(Request request) "+request.getType(), channel);
		if((getHost() instanceof RegionEditPart) && (request instanceof ChangeBoundsRequest)){
			if((request.getType() == RequestConstants.REQ_ORPHAN) 
					|| (request.getType() == RequestConstants.REQ_ORPHAN_CHILDREN)){
				Debug.debug(this, "Orphan ChangeBoundsRequest on a Region", channel);
				return null;
			}
			//this is to forbid the addition of entry/exit points on regions
			if(request.getType() == RequestConstants.REQ_ADD){
				CompoundCommand command = new CompoundCommand();
				Iterator it = ((ChangeBoundsRequest)request).getEditParts().iterator();
				while(it.hasNext()){
					EditPart currentEP = (EditPart)it.next();
					if(!(currentEP instanceof PseudostateEditPart)){
						command.add(getAddCommand(request));
					}
					else if(!((PseudostateEditPart)currentEP).isEntryOrExit()){
						command.add(getAddCommand(request));						
					}
				}
				return command;
			}
		}
		return super.getCommand(request);
	}

	/**
	 * This method provides a ghost figure over a target region when either drag and droping an already 
	 * created region, or creating a region from the palette. 
	 * 
	 * @param request the Request launched
	 */
	@Override
	public void showLayoutTargetFeedback(Request request){
		if(!(getHost() instanceof RegionEditPart))
			super.showLayoutTargetFeedback(request);
		//then two possibilities
		//either a drag and drop
		else if(request.getExtendedData().get(ObjectTransfer.UML_TRANSFER_OBJECT) != null){
			List elementList= (List)request.getExtendedData().get(ObjectTransfer.UML_TRANSFER_OBJECT);
			boolean dropRegion = false;
			Iterator<Element> it = (Iterator<Element>)elementList.iterator();
			while(!dropRegion && it.hasNext())
				if(it.next() instanceof Region)
					dropRegion = true;
			if(dropRegion){
				//feedback is an available figure for an EditPart
				IFigure feedback = getSizeOnDropFeedback();
				//retrieve the target region figure 
				RegionFigure targetFig = (RegionFigure)((RegionEditPart)getHost()).getFigure();
				//make a local copy
				Rectangle targetFigBounds = targetFig.getBounds().getCopy();
				//transform the coordinates to absolute
				targetFig.translateToAbsolute(targetFigBounds);
				Debug.debug(this, "targetFigBounds: "+targetFigBounds, channel);
				//retrieve mouse location
				Point mouseLocation = ((CreateRequest)request).getLocation();
				Debug.debug(this, "mouse at: "+mouseLocation, channel);
				//we compute to values which account for the position of the mouse location w.r.t. to the diagonals of the targetFig
				//d1 is for the first diagonal (going up)
				double d1 = mouseLocation.y-1.0*targetFigBounds.height/targetFigBounds.width*(targetFigBounds.x-mouseLocation.x)-targetFigBounds.y-targetFigBounds.height;
				//d2 is for the second (going down)
				double d2 = mouseLocation.y+1.0*targetFigBounds.height/targetFigBounds.width*(targetFigBounds.x-mouseLocation.x)-targetFigBounds.y; 
				Debug.debug(this, "d1: "+d1+" ;d2: "+d2, channel);

				//retrieve the target region node
				GraphNode targetNode = ((RegionEditPart)getHost()).getGraphNode();
				//make local copies of its position and size
				Point targetLocation = targetNode.getPosition().getCopy();
				targetLocation.translate(-1, -1);
				Dimension targetSize = targetNode.getSize().getCopy();
				targetSize.expand(3, 2);

				//and perform checks on (d1, d2)
				//based on them we perform corresponding change (scaling, translation) on targetFigBounds
				if((d1<=0) && (d2<=0)){
					//TOP
					targetSize.scale(1.0, 0.5);
				}
				else if((d1<=0) && (d2>0)){
					//LEFT
					targetSize.scale(0.5, 1.0);
				}
				else if((d1>0) && (d2<=0)){
					//RIGHT
					targetSize.scale(0.5, 1.0);
					targetLocation.translate(targetSize.width+1, 0);
				}
				else if((d1>0) && (d2>0)){
					//BOTTOM
					targetSize.scale(1.0, 0.5);
					targetLocation.translate(0, targetSize.height);
				}
				PrecisionRectangle rect = new PrecisionRectangle(new Rectangle(targetLocation, targetSize));
				//use default host figure
				targetFig.translateToAbsolute(rect);

				feedback.translateToRelative(rect);
				feedback.setBounds(rect);
			}
		}
		//or creation from the palette
		else if(RequestConstants.REQ_CREATE.equals(request.getType())){
			Object newObjectDescription = EnhancedType.getNewObjectDescription((CreateRequest)request);
			if(newObjectDescription instanceof Class){
				Class newObjectClass = (Class)newObjectDescription;
				if(newObjectClass.equals(Region.class)){
					//feedback is an available figure for an EditPart
					IFigure feedback = getSizeOnDropFeedback();
					//retrieve the target region figure 
					RegionFigure targetFig = (RegionFigure)((RegionEditPart)getHost()).getFigure();
					//make a local copy
					Rectangle targetFigBounds = targetFig.getBounds().getCopy();
					//transform the coordinates to absolute
					targetFig.translateToAbsolute(targetFigBounds);
					Debug.debug(this, "targetFigBounds: "+targetFigBounds, channel);
					//retrieve mouse location
					Point mouseLocation = ((CreateRequest)request).getLocation();
					Debug.debug(this, "mouse at: "+mouseLocation, channel);
					//we compute the values which account for the position of the mouse location w.r.t. to the diagonals of the targetFig
					//d1 is for the first diagonal (going up)
					double d1 = mouseLocation.y-1.0*targetFigBounds.height/targetFigBounds.width*(targetFigBounds.x-mouseLocation.x)-targetFigBounds.y-targetFigBounds.height;
					//d2 is for the second (going down)
					double d2 = mouseLocation.y+1.0*targetFigBounds.height/targetFigBounds.width*(targetFigBounds.x-mouseLocation.x)-targetFigBounds.y; 
					Debug.debug(this, "d1: "+d1+" ;d2: "+d2, channel);

					//retrieve the target region node
					GraphNode targetNode = ((RegionEditPart)getHost()).getGraphNode();
					//make local copies of its position and size
					Point targetLocation = targetNode.getPosition().getCopy();
					targetLocation.translate(-1, -1);
					Dimension targetSize = targetNode.getSize().getCopy();
					targetSize.expand(3, 2);

					//and perform checks on (d1, d2)
					//based on them we perform corresponding change (scaling, translation) on targetFigBounds
					//and updates the graph node drop location property
					if((d1<=0) && (d2<=0)){
						//TOP
						targetSize.scale(1.0, 0.5);
					}
					else if((d1<=0) && (d2>0)){
						//LEFT
						targetSize.scale(0.5, 1.0);
					}
					else if((d1>0) && (d2<=0)){
						//RIGHT
						targetSize.scale(0.5, 1.0);
						targetLocation.translate(targetSize.width+1, 0);
					}
					else if((d1>0) && (d2>0)){
						//BOTTOM
						targetSize.scale(1.0, 0.5);
						targetLocation.translate(0, targetSize.height);
					}
					PrecisionRectangle rect = new PrecisionRectangle(new Rectangle(targetLocation, targetSize));
					//use default host figure
					targetFig.translateToAbsolute(rect);

					feedback.translateToRelative(rect);
					feedback.setBounds(rect);
				}
			}
		}
	}
}
